package de.hsb.ai.learning;

import game.Transport;

import java.util.Arrays;
import java.util.Enumeration;

import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;

/**
 * Describes a state from the view point of the fugitive. 
 */
class State {

	public static final String SOURCE_PREFIX = "src";
	public static final String TARGET_PREFIX = "tar";

	public static final FastVector BOOLEAN_VALUES = getBooleanValues();

	public static final FastVector TRANSPORT_VALUES		= getTransportValues();
	public static final int BOOLEAN_VALUES_TRUE_INDEX	= 0;
	public static final int BOOLEAN_VALUES_FALSE_INDEX	= 1;

//	private static final Attribute hasF		= new Attribute("_hasF", BOOLEAN_VALUES);
	private static final Attribute hasB		= new Attribute("_hasB", BOOLEAN_VALUES);
	private static final Attribute hasU		= new Attribute("_hasU", BOOLEAN_VALUES);
//	private static final Attribute closest	= new Attribute("_closest");
	private static final Attribute reveal	= new Attribute("_mustReveal", BOOLEAN_VALUES);
	private static final Attribute hits		= new Attribute("_returning", BOOLEAN_VALUES);
	private static final Attribute trans	= new Attribute("_transport", TRANSPORT_VALUES);
//	private static final Attribute conn		= new Attribute("_connectivity");

	static final FastVector ATTRIBUTES = getDefaultAttributes();

	private static final FastVector getBooleanValues() {
		FastVector values = new FastVector(2);
		values.addElement(Boolean.toString(true));
		values.addElement(Boolean.toString(false));
		return values;
	}

	private static final FastVector getTransportValues() {
		FastVector values = new FastVector(6);
		values.addElement(valueOf(Transport.NONE));
		values.addElement(valueOf(Transport.TAXI));
		values.addElement(valueOf(Transport.BUS));
		values.addElement(valueOf(Transport.UG));
		values.addElement(valueOf(Transport.FERRY));
		values.addElement(valueOf(Transport.BLACK));
		return values;
	}

	private static final FastVector getDefaultAttributes() {	
		FastVector atts = new FastVector(7);
		atts.addElement(hasB);
		atts.addElement(hasU);
//		atts.addElement(hasF);
//		atts.addElement(conn);
//		atts.addElement(closest);
		atts.addElement(reveal);
		atts.addElement(hits);
		atts.addElement(trans);
		return atts;
	}

	private static double valueOf(boolean value) {
		return BOOLEAN_VALUES.indexOf(Boolean.toString(value));
	}

	private static String valueOf(int transport) {
		switch(transport) {
		case Transport.TAXI:	return "t";
		case Transport.BUS:		return "b";
		case Transport.UG:		return "u";
		case Transport.FERRY:	return "f";
		case Transport.NONE:	return "n";
		case Transport.BLACK:	return "k";
		default:
			throw new IllegalArgumentException("unknown tranportation type: " + transport);
		}
	}

	final double[] values = createValues(ATTRIBUTES.size());
	private final String prefix;
	int idx = -1;

	public State(String prefix) {
		this.prefix = prefix;
	}

	public State(String prefix, int idx) {
		this(prefix);
		this.idx = idx;
	}

	/**
	 * Creates a new vector of attributes by copying the default
	 * attributes and prefixing the name of every attribute with
	 * the specified {@code prefix}.
	 * @param prefix the attribute name prefix
	 * @return a vector of attributes
	 */
	public static FastVector getAttributes(String prefix) {
		FastVector atts = new FastVector(ATTRIBUTES.size());
		@SuppressWarnings("unchecked")
		Enumeration<Attribute> elements = ATTRIBUTES.elements();
		while (elements.hasMoreElements()) {
			Attribute att = elements.nextElement();
			atts.addElement(att.copy(prefix + att.name()));
		}
		return atts;
	}

	public static double[] createValues(int size) {
		double[] values = new double[size];
		Arrays.fill(values, Instance.missingValue());
		return values;
	}

	public FastVector getAttributes() {
		return getAttributes(prefix);
	}

	public int getIndex() {
		return idx;
	}

	public void setHasFerry(boolean hasFarry) {
		//			values[ATTRIBUTES.indexOf(hasF)] = indexOf(hasFarry);
	}

	public void setHasBus(boolean hasBus) {
		values[ATTRIBUTES.indexOf(hasB)] = valueOf(hasBus);
	}

	public void setHasUnderground(boolean hasUnderground) {
		values[ATTRIBUTES.indexOf(hasU)] = valueOf(hasUnderground);
	}

	public void setConnectivity(double level) {
//		values[ATTRIBUTES.indexOf(conn)] = level;
	}

	public void setTransport(int transport) {
		values[ATTRIBUTES.indexOf(trans)] = TRANSPORT_VALUES.indexOf(valueOf(transport));
	}

	public void setReveal(boolean hasToReveal) {
		values[ATTRIBUTES.indexOf(reveal)] = valueOf(hasToReveal);
	}

	public void setClosest(double distance) {
		distance = distance > 3 ? 0 : 1 / distance;
//		values[ATTRIBUTES.indexOf(closest)] = distance;
	}

	public void setHits(int hitCount) {
		values[ATTRIBUTES.indexOf(hits)] = valueOf(hitCount > 0);
	}

	public double getWeight() {
		return values[State.ATTRIBUTES.indexOf(State.trans)] == 0 ? 0.5 : 1;
	}

}